//go:build linux

package service

import (
	"fmt"
	"smart-flow/monitor/config"
	"smart-flow/monitor/logger"
	"strings"
	"sync/atomic"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"go.uber.org/zap"
)

type Capture struct {
	totalBytesV4 atomic.Uint64
	totalBytesV6 atomic.Uint64
	stopChan     chan struct{}
	handle       *pcap.Handle
	parser       *gopacket.DecodingLayerParser
	decoded      []gopacket.LayerType
}

func (c *Capture) InitCapture(deviceName string) bool {
	v4Ports := []string{"15550", "15551", "15552", "15553", "15554", "15555", "15556", "15557", "15558", "15559"}
	v6Ports := []string{"16550", "16551", "16552", "16553", "16554", "16555", "16556", "16557", "16558", "16559"}
	httpPorts := []string{"7171", "8181"}
	devices, _ := pcap.FindAllDevs()
	for _, dev := range devices {
		logger.Logger.Info("可用网卡", zap.String("name", dev.Name), zap.String("description", dev.Description), zap.Any("addresses", dev.Addresses))
	}
	var err error
	c.handle, err = pcap.OpenLive(deviceName, 65536, true, pcap.BlockForever)
	if err != nil {
		logger.Logger.Error("[管控] 捕获启动失败", zap.String("device", deviceName), zap.Error(err))
		return false
	}
	combinedPorts := append(v4Ports, append(v6Ports, httpPorts...)...)
	var bpfParts []string
	for _, port := range combinedPorts {
		bpfParts = append(bpfParts, fmt.Sprintf("dst port %s", port))
	}
	bpfFilter := strings.Join(bpfParts, " or ")
	logger.Logger.Info("[管控] 捕获过滤器", zap.String("filter", bpfFilter))
	if err = c.handle.SetBPFFilter(bpfFilter); err != nil {
		logger.Logger.Error("[管控] 捕获启动失败", zap.String("device", deviceName), zap.Error(err))
		return false
	}
	c.parser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &layers.Ethernet{}, &layers.Dot1Q{}, &layers.IPv4{}, &layers.IPv6{}, &layers.IPv6ExtensionSkipper{}, &gopacket.Payload{})
	c.decoded = make([]gopacket.LayerType, 0, 4)
	c.stopChan = make(chan struct{}, 1)
	var byteCount uint64
	go func() {
		for {
			select {
			case <-c.stopChan:
				return
			default:
				data, _, _ := c.handle.ZeroCopyReadPacketData()
				_ = c.parser.DecodeLayers(data, &c.decoded)
				byteCount = uint64(len(data))
				for _, typ := range c.decoded {
					switch typ {
					case layers.LayerTypeIPv4:
						c.totalBytesV4.Add(byteCount)
					case layers.LayerTypeIPv6:
						c.totalBytesV6.Add(byteCount)
					}
				}
				// logger.Logger.Info("[管控] 捕获数据", zap.Int("packet_size", len(data)), zap.Int("ci.Length", ci.Length), zap.Int("ci.CaptureLength", ci.CaptureLength))
			}
		}
	}()
	logger.Logger.Info("[管控] 捕获启动成功", zap.String("device", deviceName), zap.Int("v4_port_count", len(v4Ports)), zap.Int("v6_port_count", len(v6Ports)), zap.Int("http_port_count", len(httpPorts)))
	return true
}

func (c *Capture) StopCapture() {
	if c.stopChan != nil {
		c.stopChan <- struct{}{}
		close(c.stopChan)
	}
	c.handle.Close()
}

func (c *Capture) BandwidthV4(interval uint64) uint64 {
	return c.totalBytesV4.And(0) * config.Byte2Bit / interval
}

func (c *Capture) BandwidthV6(interval uint64) uint64 {
	return c.totalBytesV6.And(0) * config.Byte2Bit / interval
}
